package csc.approximation;

import csc.main.Colors;
import csc.main.Shift;
import java.util.ArrayList;
import java.util.List;
import org.la4j.LinearAlgebra;
import org.la4j.linear.LinearSystemSolver;
import org.la4j.matrix.dense.Basic1DMatrix;
import org.la4j.vector.Vector;
import org.la4j.vector.dense.BasicVector;

/**
 * Class'es method are used to calculate Aca and Lca approximation equations.
 *
 * @author Stanislau Dubrouski
 */
public class Approximation {

    /**
     *
     * @param shiftsForColorPairs 2D list of shifts, is used as known values for
     * calculation.
     * @param colors colors to process.
     * @return list of calculated equations. Separate equation is calculated for
     * every color pair.
     */
    public static List calculateAcaApproximationEquations(List<List<Shift>> shiftsForColorPairs, List<Colors> colors) {
        List<Basic1DMatrix> leftSideMatrixes = new ArrayList<Basic1DMatrix>();
        for (int i = 0; i < shiftsForColorPairs.size(); i++) {
            leftSideMatrixes.add(new Basic1DMatrix(shiftsForColorPairs.get(i).size(), 6));
            for (int j = 0; j < shiftsForColorPairs.get(i).size(); j++) {
                leftSideMatrixes.get(i).setRow(
                        j,
                        new BasicVector(
                                new double[]{shiftsForColorPairs.get(i).get(j).getBeginVector().getX()
                                    * shiftsForColorPairs.get(i).get(j).getBeginVector().getX(),
                                    shiftsForColorPairs.get(i).get(j).getBeginVector().getY()
                                    * shiftsForColorPairs.get(i).get(j).getBeginVector().getY(),
                                    shiftsForColorPairs.get(i).get(j).getBeginVector().getX()
                                    * shiftsForColorPairs.get(i).get(j).getBeginVector().getY(),
                                    shiftsForColorPairs.get(i).get(j).getBeginVector().getX(),
                                    shiftsForColorPairs.get(i).get(j).getBeginVector().getY(),
                                    1})
                );
            }
        }
        List<BasicVector> rightSideVectors = new ArrayList<BasicVector>();
        for (int colorPair = 0; colorPair < shiftsForColorPairs.size(); colorPair++) {
            rightSideVectors.add(new BasicVector(shiftsForColorPairs.get(colorPair).size()));
            for (int j = 0; j < shiftsForColorPairs.get(colorPair).size(); j++) {
                rightSideVectors.get(colorPair).set(j, shiftsForColorPairs.get(colorPair).get(j).computeShiftValue().getZ());
            }
        }
        List<AcaApproximationEquation> result = new ArrayList<AcaApproximationEquation>();
        for (int colorPair = 0; colorPair < shiftsForColorPairs.size(); colorPair++) {
            Colors colorFrom = colors.get(colorPair);
            Colors colorTo = (colorPair == shiftsForColorPairs.size() - 1) 
                    ? ((colors.size() == 2) ? colors.get(colorPair + 1) 
                    : colors.get(0)) : colors.get(colorPair + 1);
            LinearSystemSolver solver = leftSideMatrixes.get(colorPair).withSolver(LinearAlgebra.LEAST_SQUARES);
            Vector paramsVector = solver.solve(rightSideVectors.get(colorPair), LinearAlgebra.DENSE_FACTORY);
            result.add(new AcaApproximationEquation(
                    colorFrom,
                    colorTo,
                    paramsVector.get(0),
                    paramsVector.get(1),
                    paramsVector.get(2),
                    paramsVector.get(3),
                    paramsVector.get(4),
                    paramsVector.get(5))
            );
        }
        return result;
    }

    /**
     *
     * @param shiftsForColorPairs 2D list of shifts, is used as known values for
     * calculation.
     * @param colors colors to process.
     * @return list of calculated equations. Separate equation is calculated for
     * every color pair.
     */
    public static List calculateLcaApproximationEquations(List<List<Shift>> shiftsForColorPairs, List<Colors> colors) {
        List<Basic1DMatrix> leftSideMatrixes = new ArrayList<Basic1DMatrix>();
        for (int i = 0; i < shiftsForColorPairs.size(); i++) {
            leftSideMatrixes.add(new Basic1DMatrix(shiftsForColorPairs.get(i).size(), 3));
            for (int j = 0; j < shiftsForColorPairs.get(i).size(); j++) {
                leftSideMatrixes.get(i).setRow(j, new BasicVector(
                        new double[]{shiftsForColorPairs.get(i).get(j).getBeginVector().getX(),
                            shiftsForColorPairs.get(i).get(j).getBeginVector().getY(),
                            1})
                );
            }
        }
        ArrayList<List<BasicVector>> rightSideVectors = new ArrayList<List<BasicVector>>();
        for (int i = 0; i < shiftsForColorPairs.size(); i++) {
            rightSideVectors.add(new ArrayList<BasicVector>(shiftsForColorPairs.get(i).size()));
            rightSideVectors.get(i).add(new BasicVector(shiftsForColorPairs.get(i).size()));
            rightSideVectors.get(i).add(new BasicVector(shiftsForColorPairs.get(i).size()));
            for (int j = 0; j < shiftsForColorPairs.get(i).size(); j++) {
                rightSideVectors.get(i).get(0).set(j, shiftsForColorPairs.get(i).get(j).computeShiftValue().getX());
                rightSideVectors.get(i).get(1).set(j, shiftsForColorPairs.get(i).get(j).computeShiftValue().getY());
            }
        }
        List<List<LcaApproximationEquation>> planes = new ArrayList<List<LcaApproximationEquation>>();
        for (int colorPair = 0; colorPair < shiftsForColorPairs.size(); colorPair++) {
            Colors colorFrom = colors.get(colorPair);
            Colors colorTo = (colorPair == shiftsForColorPairs.size() - 1)
                    ? ((colors.size() == 2) ? colors.get(colorPair + 1)
                    : colors.get(0)) : colors.get(colorPair + 1);
            planes.add(new ArrayList<LcaApproximationEquation>());
            LinearSystemSolver solver = leftSideMatrixes.get(colorPair).withSolver(LinearAlgebra.LEAST_SQUARES);
            for (int shiftComponent = 0; shiftComponent < 2; shiftComponent++) {
                Vector paramsVector = solver.solve(rightSideVectors.get(colorPair).get(shiftComponent), LinearAlgebra.DENSE_FACTORY);
                planes.get(colorPair).add(new LcaApproximationEquation(
                        colorFrom, 
                        colorTo, 
                        paramsVector.get(0), 
                        paramsVector.get(1), 
                        paramsVector.get(2)));
            }
        }
        return planes;
    }

}
